package com.monster.utils;

import com.monster.common.LambdaFunction;
import com.monster.exception.impl.MonsterRuntimeException;

import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * bean对象常用处理工具
 *
 * @author kuang
 */
public class BeanUtils extends org.apache.commons.beanutils.BeanUtils {
    private BeanUtils() {
    }

    /**
     * 缓存lambda表达式的方法
     */
    private static final Map<Class, SerializedLambda> CLASS_LAMBDA_CACHE = new ConcurrentHashMap<>();

    /**
     * 修改静态参数的值
     *
     * @param field 属性
     * @return true/false
     */
    public static void setStaticFieldValue(Field field, Object value) {
        setFieldValue(null, field, value);
    }

    /**
     * 给类的属性字段赋值
     *
     * @param object 对象，静态属性这个为空
     * @param field  属性对象
     * @param value  值
     */
    public static void setFieldValue(Object object, Field field, Object value) {
        // 字段本身的修饰符
        boolean accessible = field.isAccessible();
        // 字段里面的final修饰
        Field modifiersField = null;
        boolean modifiersFieldAccessible = false;
        try {
            //将字段的访问权限设为true：即去除private修饰符的影响
            field.setAccessible(true);

            //去除final修饰符的影响，将字段设为可修改的
            modifiersField = Field.class.getDeclaredField("modifiers");
            if (modifiersField != null) {
                modifiersFieldAccessible = modifiersField.isAccessible();
                modifiersField.setAccessible(true);
                modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
            }
            // 修改值
            field.set(object, value);
        } catch (Exception e) {
            throw new MonsterRuntimeException("CORE-ATTRIBUTE-INJECTION", "属性注入错误！", e);
        } finally {
            field.setAccessible(accessible);
            // 修改final修饰
            if (modifiersField != null) {
                modifiersField.setAccessible(modifiersFieldAccessible);
            }
        }
    }

    /**
     * 获取属性的值
     *
     * @param obj   对象
     * @param field 属性
     * @return true/false
     */
    public static <T> T getFieldValue(Object obj, Field field) {
        Object result = null;
        try {
            boolean success = field.isAccessible();
            if (!success) {
                field.setAccessible(true);
            }
            result = field.get(obj);
            field.setAccessible(success);
            return (T) result;
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取属性的值
     *
     * @param beanClass 对象Class
     * @param name      属性名称
     * @return true/false
     */
    public static Field getField(Class<?> beanClass, String name) {
        try {
            return beanClass.getDeclaredField(name);
        } catch (NoSuchFieldException e) {
            return null;
        }
    }

    /**
     * 获取lambda方法
     *
     * @param fun lambda
     * @return SerializedLambda
     */
    public static SerializedLambda getSerializedLambda(Serializable fun) {
        SerializedLambda lambda = CLASS_LAMBDA_CACHE.get(fun.getClass());
        if (lambda == null) {
            Method method = null;
            try {
                method = fun.getClass().getDeclaredMethod("writeReplace");
                method.setAccessible(Boolean.TRUE);
                lambda = (SerializedLambda) method.invoke(fun);
                CLASS_LAMBDA_CACHE.put(fun.getClass(), lambda);
            } catch (Exception e) {
                throw new MonsterRuntimeException("CORE-GET-LAMBDA", "获取lambda错误！", e);
            } finally {
                if (method != null) {
                    method.setAccessible(Boolean.FALSE);
                }
            }
        }
        return lambda;
    }

    /**
     * 通过lambda获取方法名
     *
     * @param fun lambda
     * @param <T> 泛型
     * @return 方法名
     */
    public static <T> String getLambdaName(LambdaFunction<T> fun) {
        SerializedLambda lambda = getSerializedLambda(fun);
        return lambda.getImplMethodName();
    }

    /**
     * 符合规范的实体通过方法获取属性名
     *
     * @param fun lambda
     * @param <T> 泛型
     * @return 属性名称
     */
    public static <T> String getLambdaFieldName(LambdaFunction<T> fun) {
        String name = getLambdaName(fun);
        if (name.startsWith("get")) {
            name = name.substring(3);
        } else if (name.startsWith("set")) {
            name = name.substring(3);
        } else if (name.startsWith("is")) {
            name = name.substring(2);
        }
        return lowerFirstChar(name);
    }

    /**
     * 将首字母小写
     *
     * @param str 原始字符串
     * @return 首字母小写后的字符串
     */
    private static String lowerFirstChar(String str) {
        char[] chars = str.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }

    /**
     * 将首字母大写方法
     *
     * @param str 原始字符串
     * @return 首字母大写后的字符串
     */
    private static String upperFirstChar(String str) {
        char[] chars = str.toCharArray();
        chars[0] -= 32;
        return String.valueOf(chars);
    }
}
